/*
 * Created on Apr 25, 2006
 */
package net.amsoft.iservice.management.util;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.nio.channels.FileChannel;

import org.apache.log4j.Logger;
import org.apache.struts.upload.FormFile;

import com.sshtools.j2ssh.SftpClient;
import com.sshtools.j2ssh.SshClient;
import com.sshtools.j2ssh.authentication.AuthenticationProtocolState;
import com.sshtools.j2ssh.authentication.PublicKeyAuthenticationClient;
import com.sshtools.j2ssh.configuration.ConfigurationLoader;
import com.sshtools.j2ssh.configuration.SshConnectionProperties;
import com.sshtools.j2ssh.transport.publickey.SshPrivateKey;
import com.sshtools.j2ssh.transport.publickey.SshPrivateKeyFile;

/**
 * @author mukta
 */
public class FileUploadUtil {

    private static Logger logger = Logger.getLogger(FileUploadUtil.class);
    
    /**
     * Uploads modified stylesheet to a temporary file on server
     * @param fileContent
     *          Modified Stylesheet contents
     * @param outfile
     *          Temporary destination file path
     * @throws FileNotFoundException
     * @throws IOException
     */
    public static void uploadFile(String fileContent, String outfile) 
                        throws FileNotFoundException, IOException
    {
		// Write in file		    
		File dst = new File(outfile);
		OutputStream out = new FileOutputStream(dst);
		logger.info("uploadFile(): Destination Stylesheet File -> " + outfile);

		// Transfer bytes from in to out
		logger.debug("uploadFile(): Transferring stylesheet data byte by byte");
		out.write(fileContent.getBytes());
		out.close();
		
		logger.debug("uploadFile(): Stylesheet data transferred succesfully.");
    }

    /**
     * Uploads modified image to a temporary file on server
     * @param fileType
     *          Holds either "IBroker-Logo" or "Ad-Banner" or "Related-News"
     * @param myFile
     *          Input file
     * @param outfile
     *          Temporary destination file path
     * @throws FileNotFoundException
     * @throws IOException
     */
    public static void uploadFile(String fileType, FormFile myFile, String outfile) 
                        throws FileNotFoundException, IOException
    {
		// Write in file		    
		InputStream in = myFile.getInputStream();
		File dst = new File(outfile);
		OutputStream out = new FileOutputStream(dst);
		logger.info("uploadFile(): Destination " + fileType + " Image File -> " + outfile);

		// Transfer bytes from in to out
		logger.debug("uploadFile(): Transferring " + fileType + " image file byte by byte");
		byte[] buf = new byte[1024];
		int len;
		while ((len = in.read(buf)) > 0) 
		{
			out.write(buf, 0, len);
		}
		in.close();
		out.close();
		
		logger.debug("uploadFile(): " + fileType + " image file transferred succesfully.");
    }

    /**
     * Saves files in all services folders
     * @param source
     * 			Source file to be copied
     * @param imagePath
     * 			Destination type/path
     * @param independentPath
     * 			Path to save file in ibroker manager app
     * @throws FileNotFoundException 
     * @throws IOException 
     */
    public static void saveFile(String source, String filePath, String serviceName) 
                        throws FileNotFoundException, IOException
    {
		// Get server path
		String serverPath = System.getProperty("catalina.home");
		if (serverPath.indexOf('\\') >= 0)
			serverPath = serverPath.replace('\\', '/');
		serverPath = serverPath + "/webapps/";

		// Check if service folder exists on server
		logger.debug("saveFile(): Checking if \"" + serviceName + "\" folder exists on server");
		File oFile = new File(serverPath + serviceName);
		if (!(oFile.exists()))
		    oFile.mkdir();
		else if (!(oFile.isDirectory()))
		    oFile.mkdir();

		// Check whether "image" or "stylesheet" is being uploaded
		if (filePath.startsWith("i"))
		{
		    // Check if "images" folder exists
		    oFile = new File(serverPath + serviceName + "/images");
		    if (!(oFile.exists()))
		        oFile.mkdir();
			else if (!(oFile.isDirectory()))
			    oFile.mkdir();
		    
	        // Check if corressponding "server_name" folder exists
	        int i = source.lastIndexOf("/");
	        int j = source.lastIndexOf("_");
	        String serverName = source.substring(i+1,j);
	        oFile = new File(serverPath + serviceName + "/images/" + serverName);
		    if (!(oFile.exists()))
		        oFile.mkdir();
			else if (!(oFile.isDirectory()))
			    oFile.mkdir();
		}
		else
		{
		    // Check if "styleSheets" folder exists
		    oFile = new File(serverPath + serviceName + "/styleSheets");
		    if (!(oFile.exists()))
		        oFile.mkdir();
			else if (!(oFile.isDirectory()))
			    oFile.mkdir();
		}

	    logger.debug("saveFile(): Saving file for \"" + serviceName + "\" service");
        saveFile(source, serverPath + serviceName + "/" + filePath);
        
        // Synchronize file on all the servers
		logger.info("saveFile(): Calling syncFiles()");
        syncFiles(serverPath + serviceName + "/" + filePath);
        
		// Delete temporary files
		logger.info("saveFile(): Calling deleteFile()");
		deleteFile(source);
    }
    
    /**
     * Saves source file as destination file
     * @param source
     * @param dest
     * @throws FileNotFoundException 
     * @throws IOException 
     */
    private static void saveFile(String source, String dest) 
                        throws FileNotFoundException, IOException
	{
		File oFile = new File(source); 
        FileChannel infile = null;
		FileChannel outfile = null;
		 
		 if (oFile.exists())
		 {
			 logger.info("saveFile(): Saving image file \"" + source + "\" as \"" + dest + "\"");
			 try
			 {
			     logger.warn("saveFile(): Handling Files using FileInputStream()");
				 infile = new FileInputStream(source).getChannel();
	
				 logger.warn("saveFile(): Handling Files using FileOutputStream()");
				 outfile = new FileOutputStream(dest).getChannel();
				 
				 logger.warn("saveFile(): Writing file data to output file");
				 logger.info("infile size: " + infile.size() + " -> " + infile.toString());
				 logger.info("outfile size: " + outfile.size() + "-> " + outfile.toString());
				 infile.transferTo( 0, infile.size(), outfile);
				 logger.info("saveFile(): File data successfully transferred");
			 }
			 finally
			 {
			     if (infile != null)	infile.close();
				 if (outfile != null)	outfile.close();
			 }
		 }
	}

    /**
     * Deletes file in staging folder 
     * @param fileName
     */
    public static void deleteFile(String fileName)
    {
		File oFile = new File(fileName);
		if (oFile.exists())
		{
			logger.info("deleteFile(): Deleting File -> " + oFile.getName());
			oFile.delete();
		}
		else
		{
			logger.error("deleteFile(): Error while deleting file -> " + fileName + "\n File does not exist!!!");
		}
    }

    /**
     * Syncs files on all the servers
     * @param filePath
     */
    private static void syncFiles(String filePath)
    {
        String hostNames = IBrokerConstants.getProperty("appServer.hostnames").trim();
        String userName = IBrokerConstants.getProperty("appServer.username").trim();
        String keyFile = IBrokerConstants.getProperty("appServer.keyfile").trim();
        String algorithm = IBrokerConstants.getProperty("appServer.algorithm").trim();
        
        // Check for more than one servers
        if (hostNames.length() > 0)
        {
            if (hostNames.indexOf(',') > -1)
            {
                String hostName[] = hostNames.split(",");
                int num = hostName.length;
                logger.info("syncFiles(): Got " + num + " servers to sync files");
                for (int i=0; i<num; i++)
                {
                    logger.info("syncFiles(): Copying \"" + filePath + "\" to \"" + hostName[i]
                     				+ "\" with username \"" + userName + "\" using algorithm \""
                     				+ algorithm + "\"");
                    sync(hostName[i].trim(), userName, keyFile, filePath, algorithm);
                }
            }
            else
            {
                logger.info("syncFiles(): Got 1 server to sync files");
                logger.info("syncFiles(): Copying \"" + filePath + "\" to \"" + hostNames
                        		+ "\" with username \"" + userName + "\" using algorithm \""
                                + algorithm + "\"");
                sync(hostNames.trim(), userName, keyFile, filePath, algorithm);
            }   
        }
    }
    
    /**
	 * Syncs modified files on all the three servers
	 * @param hostName
	 * @param userName
	 * @param keyFile
	 * @param filePath
	 */
    private static void sync(String hostName, String userName, String keyFile, String filePath, String algorithm)
    {
        logger.info("sync(): Entering sync()");
        try
        {
            logger.info("sync(): Copying \"" + filePath + "\" to \"" + hostName
            		+ "\" with username \"" + userName + "\" using algorithm \""
                    + algorithm + "\"");

            // Get Destination Host Name
            String host = InetAddress.getLocalHost().getHostName();
            logger.info("sync(): Original Connection Host Name -> \"" + host + "\"");
            if ((host != null) && (host.indexOf('.') > -1))
            {
                host = host.trim().substring(0, host.indexOf('.'));
                logger.info("sync(): Actual Connection Host Name -> \"" + host + "\"");
            }
            
            // Skip file sync if same host
            if (!(hostName.equals(host)))
            {
                ConfigurationLoader.initialize(false);
                SshPrivateKeyFile file = SshPrivateKeyFile.parse(new File(keyFile));
                SshPrivateKey key = file.toPrivateKey(null);

                PublicKeyAuthenticationClient pk = new PublicKeyAuthenticationClient();
                pk.setUsername(userName);
                pk.setKey(key);

                SshConnectionProperties properties = new SshConnectionProperties();
	            properties.setHost(hostName);
	            properties.setPrefPublicKey(algorithm);
	
	            SshClient ssh = new SshClient();
	            ssh.setSocketTimeout (30000);
	            ssh.connect(properties);
	            int result = ssh.authenticate(pk);
	
	            if (result == AuthenticationProtocolState.COMPLETE)
	            {
	                logger.info("sync(): Login successful!!!");
	                SftpClient sftp = ssh.openSftpClient();
	                sftp.put (filePath, filePath);
	
	                logger.info("sync(): Quitting SftpClient");
	                sftp.quit();
	
	                logger.info("sync(): Disconnecting SSH");
	                ssh.disconnect ();
	                logger.info("sync(): Sync Successful!!!");
	            }
	            else
	            {
	                logger.error("sync(): Login unsuccessful!!!");
	                //System.out.println ("Key Auth : logon failed ...");
	            }
            }
            else
            {
                logger.info("sync(): Got similar hostname, thus no need to sync.");
            }
        }
        catch (Exception e)
        {
            logger.error("sync(): Exception -> " + e.getStackTrace());
        }
    }
    
}
